﻿using System;
using System.Collections.Generic;

namespace Genko.Model.Integration
{
	public abstract class AbstractMemberAdapter : IMemberAdapter
	{
		public event FillExceptionEventHandler FillException;
		private FillOptions fillOptions = null;
	
		public AbstractMemberAdapter()
		{
			
		}

		public virtual FillOptions FillOptions
		{
			get
			{
				if (this.fillOptions == null) {
					this.fillOptions = new FillOptions();
				}
				return this.fillOptions;
			}
			set
			{
				if (value == null) {
					throw new ArgumentNullException("FillOptions");
				}
				this.fillOptions = value;
			}
		}

		public abstract IMemberReader GetReader();

		public abstract IMemberWriter GetWriter();

		public virtual bool Fill(Member member)
		{
			IMemberReader rootReader = this.GetReader();
			if (rootReader == null) {
				throw new NullReferenceException("IMemberReader returned by GetReader can't be null");
			}
			if (FillOptions.RootAction == FillRootAction.Edit) {
				return Edit(member, rootReader);
			}
			return Attach(member, rootReader);
		}
		
		protected virtual bool SetAttribute(Member member, string name, string value)
		{
			try {
				member.SetAttribute(name, value);
			} catch (Exception e) {
				if (FillOptions.ExceptionAction == FillExceptionAction.Throw) {
					throw e;
				}
				OnFillException(e);
				return false;
			}
			return true;
		}
		
		protected virtual bool SetAttributes(Member member, Dictionary<string, string> attributes)
		{
			bool noErrors = true;
			foreach (KeyValuePair<string, string> attribute in attributes) {
				noErrors &= SetAttribute(member, attribute.Key, attribute.Value);
			}
			return noErrors;
		}
		
		protected virtual void OnFillException(Exception e)
		{
			FillException(this, new FillExceptionEventArgs(e));
		}
		
		protected virtual bool Attach(Member member, IMemberReader reader)
		{
			bool noErrors = true;
			try {
				Member aMember = member.CreateMember(reader.TagName);
				Guid? guid = reader.GUID;
				if (guid.HasValue) {
					noErrors &= SetAttribute(aMember, Member.GUID_ATTRIBUTE, guid.Value.ToString(Member.GUID_FORMAT));
				}
				noErrors &= SetAttributes(aMember, reader.ReadAttributes());
				member.Attach(aMember);
				foreach (IMemberReader childReader in reader) {
					noErrors &= Attach(aMember, childReader);
				}
			} catch (Exception e) {
				if (FillOptions.ExceptionAction == FillExceptionAction.Throw) {
					throw e;
				}
				OnFillException(e);
				return false;
			}
			return noErrors;
		}

		protected virtual bool Edit(Member member, IMemberReader reader)
		{
			bool noErrors = true;
			try {
				Guid? guid = reader.GUID;
				if (guid.HasValue) {
					noErrors &= SetAttribute(member, Member.GUID_ATTRIBUTE, guid.Value.ToString(Member.GUID_FORMAT));
				}
				noErrors &= SetAttributes(member, reader.ReadAttributes());
				foreach (IMemberReader childReader in reader) {
					noErrors &= Attach(member, childReader);
				}
			} catch (Exception e) {
				if (FillOptions.ExceptionAction == FillExceptionAction.Throw) {
					throw e;
				}
				OnFillException(e);
				return false;
			}
			return noErrors;
		}

		public virtual bool Update(Member member)
		{
			throw new NotImplementedException(); // TO-DO
		}
	}
}
